/**
 * Copyright (c) Huawei Technologies Co., Ltd. 2022. All Rights Reserved.
 * SPDX-License-Identifier: Apache-2.0.
 */

#include <stdio.h>
#include <string.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <sys/un.h>
#include <errno.h>
#include <stddef.h>
#include <stdarg.h>

#include "util.h"
#define ERROR_DESC_MAX_LEN  1024

static err_description_t Err_Desc[] = {
    {QT_MISSING_ARGS_ERROR, "[ E01 ]: Missing necessary argument"},
    {QT_INVALID_ARGS_ERROR, "[ E02 ]: Invalid argument provided"},
    {QT_FILE_OPS_ERROR, "[ E03 ]: File operation failure"},
    {QT_IOCTL_GET_SANDBOX_CAP_ERROR, "[ E04 ]: Ioctl get sandbox capacity failure"},
    {QT_IOCTL_DEFINE_SANDBOX_ERROR, "[ E05 ]: Ioctl define sandbox failure"},
    {QT_INVALID_PARS_ERROR, "[ E06 ]: Invalid parameters provided in configuration file"},
    {QT_MISSING_ARGS_IN_CONF_ERROR, "[ E07 ]: Missing necessary parameters in configuration file"},
    {QT_MMAP_MEM_ERROR, "[ E08 ]: Mmap memory failure"},
    {QT_IOCTL_ADD_MEM_ERROR, "[ E09 ]: Ioctl add memory failure"},
    {QT_LOAD_IMAGE_ERROR, "[ E10 ]: Load image failure because provided memory is too small"},
    {QT_IOCTL_ADD_CPU_ERROR, "[ E11 ]: Ioctl add cpu failure"},
    {QT_LOCK_ACQUIRE_ERROR, "[ E12 ]: Lock acquire failure"},
    {QT_SOCKET_INIT_ERROR, "[ E13 ]:  Socket initialization failure"},
    {QT_SOCKET_BIND_ERROR, "[ E14 ]: Socket binding failure"},
    {QT_SOCKET_LISTEN_ERROR, "[ E15 ]: Socket listen failure"},
    {QT_SOCKET_ACCEPT_ERROR, "[ E16 ]: Socket accept failure"},
    {QT_WRITE_HEARTBEAT_ERROR, "[ E17 ]: Write heartbeat to the enclave failure"},
    {QT_READ_HEARTBEAT_ERROR, "[ E18 ]: Read heartbeat from the enclave failure"},
    {QT_IOCTL_START_ERROR, "[ E19 ]: Ioctl start an enclave failure"},
    {QT_WAIT_HEARTBEAT_TIMEOUT_ERROR, "[ E20 ]: Wait heartbeat timeout"},
    {QT_GET_JSON_OBJ_ERROR, "[ E21 ]: Get json print object failure"},
    {QT_WRITE_ENCLAVE_CONFIG_ERROR, "[ E22 ]: Write enclave's configuration file failure"},
    {QT_SOCKET_CONNECT_ERROR, "[ E23 ]: Socket connection failure"},
    {QT_WRITE_CMD_TO_ENCLAVE_ERROR, "[ E24 ]: Write cmd to the enclave server failure"},
    {QT_READ_INFO_FROM_ENCLAVE_ERROR, "[ E25 ]: Read message from the enclave server failure"},
    {QT_CREATE_CJSON_OBJ_ERROR, "[ E26 ]: Create cjson object failure"},
    {QT_CREATE_CJSON_ARR_ERROR, "[ E27 ]: Create cjson array failure"},
    {QT_ENCLAVE_NOT_RUNNING_ERROR, "[ E28 ]: The required enclave is not running"},
    {QT_INVALID_ENCLAVE_PID_ERROR, "[ E29 ]: Invalid enclave pid"},
    {QT_ADD_NUM_TO_CJSON_OBJ_ERROR, "[ E30 ]: Add number into cjson object failure"},
    {QT_ADD_STR_TO_CJSON_OBJ_ERROR, "[ E31 ]: Add string into cjson object failure"},
    {QT_NOT_IN_DEBUG_MODE_ERROR, "[ E32 ]: The required enclave is not running in the debug mode"},
    {QT_CONSOLE_READ_ERROR, "[ E33 ]: Enclave console read failure"},
    {QT_WRITE_IMG_HDR_ERROR, "[ E34 ]: Write img header failure"},
    {QT_WRITE_CMDLINE_ERROR, "[ E35 ]: Write cmdline failure"},
    {QT_WRITE_KERNEL_ERROR, "[ E36 ]: Write kernel failure"},
    {QT_WRITE_INITTRD_ERROR, "[ E37 ]: Write initrd failure"},
    {QT_WRITE_CERT_ERROR, "[ E38 ]: Write certificate failure"},
    {QT_GET_PCR_ERROR, "[ E39 ]: Get pcr failure"},
    {QT_ADD_SIG_ERROR, "[ E40 ]: Add signature failure"},
    {QT_QTBOX_SANDBOX_OPS_ERROR, "[ E41 ]: The required enclave is in maintenance state"},
    {QT_SUCCESS, NULL}};

int serv_accept(int listen_fd)
{
    int fd;
    socklen_t len;
    struct sockaddr_un un;

    len = sizeof(un);
    fd = accept(listen_fd, (struct sockaddr *)&un, &len);
    if (fd < 0) {
        qt_log_err(QT_SOCKET_ACCEPT_ERROR, QT_DISPLAY_ADDITIONAL_ERROR,
                   "Accept failed, errno:%d [%m]", errno);
        return -1;
    }

    return fd;
}

int serv_listen(const char *name)
{
    int fd;
    socklen_t len;
    struct sockaddr_un un;

    if (name == NULL || strlen(name) >= sizeof(un.sun_path)) {
        qt_log_err(QT_UNDEINED_ERROR_NUM, QT_NOT_DISPLAY_ADDITIONAL_ERROR, "Invalid args");
        return -1;
    }

    fd = socket(AF_UNIX, SOCK_STREAM, 0);
    if (fd < 0) {
        qt_log_err(QT_UNDEINED_ERROR_NUM, QT_NOT_DISPLAY_ADDITIONAL_ERROR,
                   "socket fails, errno: %d [%m]", errno);
        return -1;
    }

    unlink(name);
    memset(&un, 0, sizeof(un));
    un.sun_family = AF_UNIX;
    strncpy(un.sun_path, name, sizeof(un.sun_path) - 1);
    len = offsetof(struct sockaddr_un, sun_path) + strlen(name);

    if (bind(fd, (struct sockaddr *)&un, len) < 0) {
        qt_log_err(QT_UNDEINED_ERROR_NUM, QT_NOT_DISPLAY_ADDITIONAL_ERROR,
                   "Socket bind failed, errno: %d [%m]", errno);
        goto out;
    }

    if (listen(fd, CONNECT_NUM) < 0) {
        qt_log_err(QT_UNDEINED_ERROR_NUM, QT_NOT_DISPLAY_ADDITIONAL_ERROR,
                   "listen failed, errno: %d [%m]", errno);
        goto out;
    }

    return fd;
out:
    close(fd);
    return -1;
}

int cli_conn(const char *name)
{
    int fd;
    socklen_t len;
    struct sockaddr_un un;

    if (!name || strlen(name) >= sizeof(un.sun_path)) {
        errno = ENAMETOOLONG;
        return -1;
    }

    fd = socket(AF_UNIX, SOCK_STREAM, 0);
    if (fd < 0) {
        qt_log_err(QT_SOCKET_INIT_ERROR, QT_NOT_DISPLAY_ADDITIONAL_ERROR,
                   "Socket initialization failed: [%m]");
        return -1;
    }

    memset(&un, 0, sizeof(un));
    un.sun_family = AF_UNIX;
    len = offsetof(struct sockaddr_un, sun_path) + strlen(name);
    strncpy(un.sun_path, name, sizeof(un.sun_path) - 1);

    if (connect(fd, (struct sockaddr *)&un, len) < 0) {
        qt_log_err(QT_SOCKET_CONNECT_ERROR, QT_NOT_DISPLAY_ADDITIONAL_ERROR,
                   "Socket connection failed: [%m]");
        goto out;
    }

    return fd;

out:
    close(fd);
    return -1;
}

static void display_errno_str(int err_num, const char *funcname, int linenr)
{
    err_description_t *c;

    for (c = Err_Desc; c->err_description != NULL; c++) {
        if (c->err_num == err_num) {
            printf("%s\n\n", c->err_description);
            enclave_log(LOG_ERR, funcname, linenr, c->err_description);
            return;
        }
    }
}

void display_err_info(int err_num, const char *funcname, int linenr, qt_display_t flag, const char *fmt, ...)
{
    char buf[ERROR_DESC_MAX_LEN + 1] = {0};
    va_list ap;

    display_errno_str(err_num, funcname, linenr);
    va_start(ap, fmt);
    (void)vsnprintf(buf, ERROR_DESC_MAX_LEN, fmt, ap);
    va_end(ap);
    if (flag == QT_DISPLAY_ADDITIONAL_ERROR)
        printf("%s\n\n", buf);
    enclave_log(LOG_ERR, funcname, linenr, "%s", buf);
    printf("You can find corresponding error log entities in \"/var/log/qingtian_enclaves/qingtian-tool.log\" \n\n");
    printf("For more details, please visit our website. \n\n");
}